/**
 * Exam No: Y8467136
 * 
 * Windows
 * Java Version: JavaSE-1.6
 * Eclipse-SDK-3.7-win32-x86_64
 */
package japrc2011;


import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.JToggleButton;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;
import javax.swing.JPanel;
import java.awt.BorderLayout;
import javax.swing.border.TitledBorder;
import javax.swing.UIManager;
import javax.swing.JScrollPane;

public final class RobotSimulatorGUI extends JFrame implements SimGUIInterface
{
	// Declaration of JButtons
    private JButton southButton;//declare the south button. This button moves the robot to the south
    private JButton northButton;//declare the north button. This button moves the robot to the north 
    private JButton eastButton;//declare the east button. This button moves the robot to the east 
    private JButton westButton;//declare the west button. This button moves the robot to the west 
    private JButton testButton; 
    private JButton selfDestructButton;
    private JButton exitButton;
    private JButton tickButton; 
    private JButton addButton;
    private JButton addWallButton; 
    private JButton modeButton;
    private JButton ifButton;
    private JButton changeMapSize;
    private JButton repeatButton;
    private JButton stopWaitRepeat;
    private JButton waitButton;
    private JToggleButton startStopButton;//start and stop button. While pressed the robot is in start mode. If we press the button again(not pressed) the robot stops.
    //Declaration of JLabels
    private JLabel currentCommandCount; //Label in which the current command count appeares.
    private JLabel simulationTime;  //Label in the GUI with static content. "Simulation Time"
    private JLabel currentSimulationTime; //Label in which the current simulation time appears.
    private JLabel commandMode; //Label in which the current mode appears.
    private JLabel newRobot; //Label with static content "Add Robot: ".
    private JLabel newWall; // Label with static content "Add Wall: ".
    //Declaration of JTextFields
    private JTextField theNewRobot; //TextField which accepts the name of the new added robot.
    private JTextField theNewWall; // TextField which accepts the dimension to set the new wall.

    private JTable mapView;
    private MapTableModel mapTableModel;
    
    private RobotSimulator sim;
    static String myMode = "Immediate Mode"; //When the program starts the mode is "Immediate Mode"
    int mapX;
    int mapY;
    Thread t1; // thread used to refresh the simulation time on start mode every second.
    Thread t2; // thread to refresh the sim time and the command count when a REPEAT command is executed.
    private JPanel panel;
    private JPanel panel_1;
    private JPanel panel_2;
    private JPanel panel_3;
    private JPanel panel_5;
    private JPanel panel_6;
    private JPanel panel_7;
    private JPanel panel_4;
    private JScrollPane scrollPane;
    
    /** CONSTRUCTOR */
    public RobotSimulatorGUI(RobotSimulator simToUse)
    {
        super("Robot Simulator GUI");
     
        this.sim = simToUse;
        
        /** ADD ROBOT */ 
        addButton = new JButton("New Robot");
        addButton.setBounds(855, 84, 99, 23);
        addButton.addActionListener(new ActionListener() { 
        	@Override 
        	public void actionPerformed(ActionEvent arg0) 
        	{ //when the button is pressed, get the value of the theNewRobot textField.
        		String newRobot = theNewRobot.getText(); 
        		//checks if the textField is empty or not!
        		if(newRobot.equals("")) //if it empty it doesn't do anything
        		{ 
        			
        		} 
        		else //else it adds the robot in the GUI calling the addRobot method.
        		{ 
        			sim.addRobot(new Robot(newRobot)); 
        		} 
        		
        	} 
        });
        getContentPane().setLayout(null);
        getContentPane().add(addButton); 
        
        /** ADD WALL */ 
        addWallButton = new JButton("Add Wall");
        addWallButton.setBounds(855, 156, 99, 23);
        addWallButton.addActionListener(new ActionListener() { 
        	@Override 
        	public void actionPerformed(ActionEvent arg0) 
        	{ 
        		String textwall = theNewWall.getText(); //takes the text from the textfield
        		//checks if the textField is empty or not!
        		if(textwall.equals("")) 
        		{ 
        			
        		}
        		else //if the textField textwall is not empty the program tries to add the wall
        		{
        			String[] k = textwall.split(","); //it makes the spit to take the actual dimensions
        			int wall_x = Integer.parseInt(k[0]); //converts the string characters to int
        			int wall_y = Integer.parseInt(k[1]); //converts the string characters to int
        			sim.addWall(new GridLocation(wall_x, wall_y)); // and calls the addwall method passing the dimensions the user asked
        		}
        	} 
        }); 
        getContentPane().add(addWallButton); 
        
        /** CHANDE_MODE BUTTON */ 
        modeButton = new JButton("Change Mode");
        modeButton.setBounds(14, 10, 113, 23);
        modeButton.addActionListener(new ActionListener() { 
        	@Override 
        	public void actionPerformed(ActionEvent arg0) 
        	{ 
        		if(startStopButton.isSelected()) //disable the Immediate/Program button while in start() mode.
        		{ 
        			
        		} 
        		else //change the mode from "Immediate Mode" to "Program Mode" or vise versa. The textFiled shows the current mode.
        		{ 
        			if(commandMode.getText().equals("Immediate Mode")) 
        			{ 
        				commandMode.setText("Program Mode"); 
        				myMode = "Program Mode"; 
        			} 
        			else 
        			{ 
        				commandMode.setText("Immediate Mode"); 
        				myMode = "Immediate Mode"; 
        			} 
        		} 
        	} 
        }); 
        getContentPane().add(modeButton); 
        
        /** EXIT BUTTON */
        exitButton = new JButton("Exit Program");
        exitButton.setBounds(841, 597, 113, 23);
        exitButton.addActionListener(new ActionListener() {            
            @Override
            public void actionPerformed(ActionEvent arg0)
            {
                System.exit(0);               
            }
        });
        getContentPane().add(exitButton);
        
//---------------------------------------------------------------------------------------------------------------------
        
        /** LABEL NEW ROBOT */
        newRobot = new JLabel("Add Robot: ");
        newRobot.setBounds(776, 56, 68, 14);
        getContentPane().add(newRobot); 
        
        /** ENTRY TEXTFIELD FOR NEW ROBOT */
        theNewRobot = new JTextField();
        theNewRobot.setBounds(854, 51, 100, 25);
        theNewRobot.setPreferredSize(new Dimension(100, 25)); 
        getContentPane().add(theNewRobot);
        
        /** LABEL NEW WALL */
        newWall = new JLabel("Add Wall: ");
        newWall.setBounds(776, 128, 68, 14);
        getContentPane().add(newWall);
        
        /** ENTRY TEXTFIELF FOR NEW WALL */
        theNewWall = new JTextField();
        theNewWall.setBounds(854, 123, 100, 25);
        theNewWall.setPreferredSize(new Dimension(100, 25)); 
        theNewWall.setToolTipText("x,y");//when you move the mouse on the text field a tip message is appeared
        getContentPane().add(theNewWall);
        
        /** MAP */
        mapTableModel = new MapTableModel();
        updateMap();
        
        /** COMMAND_COUNT SCREEN - FRAME */
        currentCommandCount = new JLabel();
        currentCommandCount.setBounds(855, 230, 100, 40);
        Border border = BorderFactory.createTitledBorder("Commands");
        currentCommandCount.setBorder(border);
        currentCommandCount.setPreferredSize(new Dimension(100,40));
        getContentPane().add(currentCommandCount);
        
        /** SIMULATION TIME LABEL */
        simulationTime = new JLabel("Simulation Time:");
        simulationTime.setBounds(331, 256, 99, 14);
        getContentPane().add(simulationTime);
        
        /** SIMULATION TIME SCREEN - BLACK_LABEL*/
        currentSimulationTime = new JLabel();
        currentSimulationTime.setBounds(454, 250, 100, 20);
        currentSimulationTime.setPreferredSize(new Dimension(100,20));
        currentSimulationTime.setForeground(Color.white);
        currentSimulationTime.setBackground(Color.black);
        currentSimulationTime.setOpaque(true);
        getContentPane().add(currentSimulationTime);
        
        /** MODE SCREEN - BLACK_LABEL */ 
        commandMode = new JLabel("Immediate Mode",JLabel.CENTER);
        commandMode.setBounds(151, 11, 200, 20);
        commandMode.setPreferredSize(new Dimension(200,20)); 
        commandMode.setForeground(Color.white); 
        commandMode.setBackground(Color.black); 
        commandMode.setOpaque(true); 
        getContentPane().add(commandMode);
        
//-------------------------------------------------------------Panel 5--------------------------------------------------
        //This panel contains one more panel with the buttons south, west, east, north and tick.
        panel_5 = new JPanel();
        panel_5.setBounds(10, 44, 200, 112);
        panel_5.setBorder(new TitledBorder(UIManager.getBorder("TitledBorder.border"), "Move Buttons", TitledBorder.LEADING, TitledBorder.TOP, null, new Color(0, 0, 0)));
        getContentPane().add(panel_5);
        panel_5.setLayout(null);
        
        panel = new JPanel();
        panel.setBounds(6, 20, 188, 79);
        panel_5.add(panel);
        panel.setLayout(new BorderLayout(0, 0));
        
        /** SOUTH BUTTON */ 
        southButton = new JButton("South");
        panel.add(southButton, BorderLayout.SOUTH);
        southButton.addActionListener(new ActionListener() {    
            @Override
            public void actionPerformed(ActionEvent arg0)
            {
            	//in program mode the commands are executed when the tick button is pressed
            	String cmd = "M 0 1";
        		sim.sendCommand("r1", cmd);
            	if(myMode == "Immediate Mode")
            	{ 
            		sim.tick(); //in immediate mode calls the tick() when the button is pressed(immediate)
            		currentSimulationTime.setText(String.valueOf(sim.getSimTime()) + " " + "secs"); 
            	} 
            	currentCommandCount.setText(String.valueOf(sim.getRobotInfo("r1", "COMMAND_COUNT"))); 
            }
        });
        southButton.setEnabled(true);
        
        /** WEST BUTTON*/
        westButton = new JButton("West");
        panel.add(westButton, BorderLayout.WEST);
        westButton.addActionListener(new ActionListener(){ 
        	@Override 
        	public void actionPerformed(ActionEvent arg0) 
        	{ //--
        		String cmd = "M -1 0"; 
    			sim.sendCommand("r1", cmd); 
        		if(myMode == "Immediate Mode") 
        		{ 
         			sim.tick(); 
                    currentSimulationTime.setText(String.valueOf(sim.getSimTime()) + " " + "secs"); 
        		}
        		currentCommandCount.setText(String.valueOf(sim.getRobotInfo("r1", "COMMAND_COUNT"))); 
        	} 
        });
        
        /** EAST BUTTON */
        eastButton = new JButton("East");
        panel.add(eastButton, BorderLayout.EAST);
        eastButton.addActionListener(new ActionListener(){ 
        	@Override 
        	public void actionPerformed(ActionEvent arg0)
        	{ //--
        		String cmd = "M 1  0"; 
    			sim.sendCommand("r1", cmd); 
        		if(myMode == "Immediate Mode") 
        		{ 
         			sim.tick(); //--
                    currentSimulationTime.setText(String.valueOf(sim.getSimTime()) + " " + "secs"); 
        		} 
        		currentCommandCount.setText(String.valueOf(sim.getRobotInfo("r1", "COMMAND_COUNT"))); 
        	} 
        });
        
        /** NORTH BUTTON */
        northButton = new JButton("North");
        panel.add(northButton, BorderLayout.NORTH);
        northButton.addActionListener(new ActionListener(){
       	 @Override 
            public void actionPerformed(ActionEvent arg0) 
            { 
       		 String cmd = "M 0 -1"; 
        		 sim.sendCommand("r1", cmd); 
        		 if(myMode == "Immediate Mode")
        		 { 
        			sim.tick();
                   currentSimulationTime.setText(String.valueOf(sim.getSimTime()) + " " + "secs"); 
        		 } 
        		 currentCommandCount.setText(String.valueOf(sim.getRobotInfo("r1", "COMMAND_COUNT"))); 
            } 
       });
        
        /** TICK BUTTON */
        tickButton = new JButton("Tick");
        panel.add(tickButton, BorderLayout.CENTER);
        tickButton.addActionListener(new ActionListener(){ 
        	@Override 
        	public void actionPerformed(ActionEvent arg0) 
        	{ 
        		if(startStopButton.isSelected()) //disable the Tick button while in start mode
        		{ 
        			
        		} 
        		else 
        		{ 
        			if(myMode == "Program Mode") //if the Program mode is selected the tick button calls the tick() method of the RobotSimulator class.
        			{ 
        				sim.tick(); //--
        				currentSimulationTime.setText(String.valueOf(sim.getSimTime()) + " " + "secs"); 
        			} 
        		} 
        	} 
        });
        
//---------------------------------------------------------Panel 6------------------------------------------------------
      
        //This panel contains two more panels, panel_2 and panel_1.
        //The panel_2 contains the WAIT, REPEAT and STOP W-R buttons.
        panel_6 = new JPanel();
        panel_6.setBounds(218, 44, 180, 197);
        panel_6.setBorder(new TitledBorder(UIManager.getBorder("TitledBorder.border"), "Extra Buttons", TitledBorder.LEADING, TitledBorder.TOP, null, new Color(0, 0, 0)));
        getContentPane().add(panel_6);
        panel_6.setLayout(null);
        
        panel_2 = new JPanel();
        panel_2.setBounds(6, 20, 164, 63);
        panel_6.add(panel_2);
        
        /**WAIT */
        //When this button is pressed a WAIT command is sent telling to the robot to wait for some specified time.
        waitButton = new JButton("WAIT");
        panel_2.add(waitButton);
        waitButton.addActionListener(new ActionListener() { 
        	@Override 
        	public void actionPerformed(ActionEvent arg0) 
        	{ 
        		String cmd = "WAIT 10000"; 
        		sim.sendCommand("r1", cmd); 
        		currentCommandCount.setText(String.valueOf(sim.getRobotInfo("r1", "COMMAND_COUNT")));
        	}
        });
        
        /** REPEAT BUTTON */
        //When this button is pressed, a REPEAT command is sent to the robot. This command makes the robot to repeat a move.
        repeatButton = new JButton("REPEAT");
        panel_2.add(repeatButton);
        repeatButton.addActionListener(new ActionListener() {
        	@Override
        	public void actionPerformed(ActionEvent arg0)
        	{
        		String cmd = "REPEAT (M 1 0) (2000)"; 
        		sim.sendCommand("r1", cmd);       
        		t2 = new Thread() //thread used to refresh the sim time and the command count in the GUI 
        		{ 
        			public void run() 
        			{ 
        				while(sim.t1.isAlive()) //while it is alive refresh the values of the labels.
        				{
        					currentSimulationTime.setText(String.valueOf(sim.getSimTime()) + " " + "secs"); //the simulation time appears as number of seconds in the simulation time label
        					currentCommandCount.setText(String.valueOf(sim.getRobotInfo("r1", "COMMAND_COUNT"))); 
        				}
        			} 
        		};
        		t2.start();        		
        	}
        });
        
        /** STOP WAIT-REPEAT */
        //Pressing this button a STOP command is sent to the robot, in order to stop any active WAIT or REPEAT
        stopWaitRepeat = new JButton("STOP W-R");
        panel_2.add(stopWaitRepeat);
        stopWaitRepeat.addActionListener(new ActionListener() { 
        	@Override 
        	public void actionPerformed(ActionEvent arg0) 
        	{ 
        		String cmd = "STOP";
        		sim.sendCommand("r1", cmd);
        	}
        });
        
        //The panel_1 contains the IF button.
        panel_1 = new JPanel();
        panel_1.setBounds(6, 94, 164, 63);
        panel_6.add(panel_1);
        
        /** IF BUTTON */
        ifButton = new JButton();
        panel_1.add(ifButton);
        ifButton.setText("IF");
        ifButton.addActionListener(new ActionListener(){
        	public void actionPerformed(ActionEvent arg0)
        	{
        		String cmd = "IF (BLOCKED_WEST) (SELF_DESTRUCT)"; 
        		sim.sendCommand("r1", cmd);
        			//sim.tick();
        		currentCommandCount.setText(String.valueOf(sim.getRobotInfo("r1", "COMMAND_COUNT")));
        		currentSimulationTime.setText(String.valueOf(sim.getSimTime()) + " " + "secs"); 
        	}
        });

//---------------------------------------------------------panel_7------------------------------------------------------
        
        //The panel_7 contains one more panel the panel_3.
        panel_7 = new JPanel();
        panel_7.setBounds(12, 165, 195, 76);
        panel_7.setBorder(new TitledBorder(UIManager.getBorder("TitledBorder.border"), "", TitledBorder.LEADING, TitledBorder.TOP, null, new Color(0, 0, 0)));
        getContentPane().add(panel_7);
        panel_7.setLayout(null);
        
        //The panel_3 contains the Self-Destruct, Test, Start buttons.
        panel_3 = new JPanel();
        panel_3.setBounds(20, 5, 156, 63);
        panel_7.add(panel_3);
        
        /** SELF-DESTRUCT BUTTON */
        selfDestructButton = new JButton("Self-Destruct");
        panel_3.add(selfDestructButton);
        selfDestructButton.addActionListener(new ActionListener(){ 
        	@Override 
        	public void actionPerformed(ActionEvent arg0) 
        	{ 
        		String cmd = "SELF_DESTRUCT"; 
        		sim.sendCommand("r1", cmd); 
        		//sim.sendCommand("r1", "SELF_DESTRUCT"); 
        		currentCommandCount.setText(String.valueOf(sim.getRobotInfo("r1", "COMMAND_COUNT"))); 
        	} 
        });
        
        /** TEST BUTTON */
        testButton = new JButton("Test");
        panel_3.add(testButton);
        testButton.addActionListener(new ActionListener(){ 
        	@Override 
        	public void actionPerformed(ActionEvent arg0) 
        	{ 
        		String cmd = "TEST  "; 
        		sim.sendCommand("r1", cmd); 
        		//sim.sendCommand("r1", "TEST"); 
        		currentCommandCount.setText(String.valueOf(sim.getRobotInfo("r1", "COMMAND_COUNT"))); 
        	} 
        });
        
        /** START - STOP BUTTON */
        startStopButton = new JToggleButton();//a toggle button. It has two states: start and stop!
        panel_3.add(startStopButton);
        startStopButton.setText("Start");
        startStopButton.addItemListener(new ItemListener() { 
        	@Override 
        	public void itemStateChanged(ItemEvent arg0) 
        	{ 
        		int state = arg0.getStateChange(); 
        		if (state == ItemEvent.SELECTED) // if it is selected - pressed then...
        		{ 
        			commandMode.setText("Start Mode"); //the "Start Mode" is set to the label commandMode.
        			myMode = "Start Mode"; 
            		startStopButton.setText("Stop "); //the title of the button changes to "Stop ".
            		sim.start(); //and the method start() of the RobotSimulator class is called.
            		t1 = new Thread() //create and start a thread to refresh the text of the label currentSimulationTime(simulation time) every second.
            		{ 
            			public void run() 
            			{ 
            				while(startStopButton.isSelected()) 
            					currentSimulationTime.setText(String.valueOf(sim.getSimTime()) + " " + "secs"); //the simulation time appears as number of seconds in the simulation time label
            			} 
            		};
            		t1.start(); 
        		} 
        		else //if it is not selected - pressed...
        		{ 
        			commandMode.setText("Program Mode"); //set the text if the textField commandMode as "Program Mode"
        			myMode = "Program Mode"; //change the value of the myMode that identifies to the program, which module is selected.
        			startStopButton.setText("Start"); //the title of the button changes to "Start".
        			sim.stop(); //calls the stop() of the RobotSimulator class.
        		} 
        	} 
        });
        
        /** INCREASE MAP SIZE BUTTON */ 
        changeMapSize = new JButton("Map Size");
        changeMapSize.setBounds(14, 247, 90, 23);
        //When this button is pressed, increases the size of the map.
        changeMapSize.addActionListener(new ActionListener() { 
        	public void actionPerformed(ActionEvent arg0) 
        	{ 
        		mapY = mapTableModel.getColumnCount() + 1; //increases by one the x.
        		mapX = mapTableModel.getRowCount() + 1; // increases by one the y
        		sim.setMapSize(mapX, mapY); //calls the setMapSize() method with the new dimensions.
        		mapTableModel = new MapTableModel(); //recreates the map, in order the new map with the new dimensions to be set.
        		mapView.setModel(mapTableModel); 
        		updateMap(); 
        	} 
        });
        getContentPane().add(changeMapSize);
        
        //A scrollPane which contains the panel_4.
        scrollPane = new JScrollPane();
        scrollPane.setBounds(12, 281, 942, 295);
        getContentPane().add(scrollPane);
        
        //The panel_4 contains the JTable.
        panel_4 = new JPanel();
        scrollPane.setViewportView(panel_4);
        panel_4.setBorder(new TitledBorder(UIManager.getBorder("TitledBorder.border"), "Map Area", TitledBorder.LEADING, TitledBorder.TOP, null, new Color(0, 0, 0)));
        panel_4.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));
        mapView = new JTable(mapTableModel);
        panel_4.add(mapView);
        
    }
    
    public void updateMap()
    {
        MapEntity[][] newMapView = sim.getMapState();//takes all the objects that included in the map(returned from the getMapState();) and saves them in the newMapView variable
        //TODO: make this cope with a map of any size
        //refresh the content of the map
        for(int i=0; i<mapTableModel.getColumnCount(); ++i) { 
            for(int j=0; j<mapTableModel.getRowCount(); ++j) { 
                //translate from the (col,row) view used by the sim to the (row,col) view used by JTable
                MapEntity m = newMapView[i][j];
                if(m==null)
                {
                    mapTableModel.setValueAt("", j, i);    
                }
                else
                {
                    mapTableModel.setValueAt(m, j, i);
                }
            }
            
        }
    }
    
    
    public static void main(String[] args)
    {
        RobotSimulator sim = new RobotSimulator();        
        sim.addRobot(new Robot("r1"));
        sim.setMapSize(3, 3); //when the program starts the map is 3x3
        sim.addWall(new GridLocation(0,1)); //--
        
        System.out.println("The current Thread is: " + Thread.currentThread().getName());
        
        RobotSimulatorGUI gui = new RobotSimulatorGUI(sim);
        gui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        gui.setSize(980,670);
        sim.setGUI(gui);
                
        gui.setVisible(true);
    }

    @Override
    public void notifySimHasChanged()// overrides the method notifySimHasChanged of the interface SimGUIInterface 
    {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                updateMap();
            }
        });
    } 
}
